/*
 Автор: Швейкин Евгений Юрьевич

 Функциональное описание:     
    Модуль мониторит фифо, а если в нем есть данные, записывает их по амм. 
    Данные могут подаваться с задержкой, запись пачки начнется только после накопления нужного 
кол-ва данных.
    В начале данные пишутся пачками максимальной длины, остаток пачкой нужного размера.

На вход модуля подается:
 - количество данных, которое необходимо записать, в 32-хбитных словах;
 - адрес памяти куда записывать, в Байтах;
 - запускающий импульс.

*/
//--------------------------------------------------------------------------------------------------


// *********************** НАЧАЛО МОДУЛЯ ***********************************************************
module fifo_to_amm_burst
#(  parameter               max_burst_p     = 32,       // максимальный размер пачки
    parameter               fifo_size_p     = 1024      // Размер фифо
)
(
    input  logic            reset_n,
    input  logic            amm_clk,
    
    // Управление модулем
    input  logic            start_i,
    input  logic [31:0]     data_size_i,        // Слов
    input  logic [31:0]     base_addr_i,        // Байт
    output logic            busy,    
    
    // Порты фифо
    input  logic            data_clk,
    input  logic            wrreq_i,
    input  logic [31:0]     data_i,             // Данные записываемые в фифо
    
    output logic            wrfull_o,           // Фифо заполнено
    
    output logic            rdreq_o,            // Запрос нового отсчета данных
    
    // Сигналы шины master авалон
    input  logic            avm_waitrequest,
    
    output logic [31:0]     avm_address,
    output logic            avm_write,
    output logic [9:0]      avm_burstcount,
    output logic            avm_beginbursttransfer,
    output logic[31:0]      avm_writedata
);
 
//  ********************* КОНСТАНТЫ ****************************************************************
    localparam fifo_size_lp     = fifo_size_p;
    localparam fifo_wa_lp       = $clog2(fifo_size_lp);
    
//  ********************* ФУНКЦИИ ******************************************************************

//  ********************* ТИПЫ ДАННЫХ **************************************************************
    // Состояния автомата считывания пакетов из фифо и передачи их по Avalon
    enum logic [4:0]    {   IDLE_s              = 5'b00001,
                            AMM_REQ_WR_BURST_s  = 5'b00010,
                            AMM_WR_BURST_s      = 5'b00100,
                            CALC_s              = 5'b01000,
                            WAIT_s              = 5'b10000
                        } state_fifo_to_amm_burst, next_state;

    typedef struct packed 
    {   logic [31:0]                data;
        logic                       wrreq;
        logic [fifo_wa_lp - 1:0]    wrusedw;
        logic                       wrfull;
        
        logic [31:0]                q;
        logic                       rdreq;
        logic [fifo_wa_lp - 1:0]    rdusedw;
        logic                       rdempty;
    } fifo_t;
    
// ********************* ПЕРЕМЕННЫЕ ****************************************************************
    fifo_t                          fifo;
    
    logic                           soft_reset; 
    
    logic [9:0]                     burst_word_cnt;
    logic [9:0]                     current_burstcount;
    
    logic                           rdempty;
    
    logic                           end_burst;
    logic                           end_data;
    logic [31:0]                    address;
    
    logic [31:0]                    data_size_latch;
    logic [31:0]                    data_word_cnt;
    logic                           reset_fifo;
// ********************* ИНИЦИАЛИЗАЦИЯ ПЕРЕМЕННЫХ ДЛЯ СИМУЛЯТОРА ***********************************

    
//  ********************* ASSIGN *******************************************************************
    assign fifo.data    = data_i;
    assign fifo.wrreq   = wrreq_i;
    assign wrfull_o     = fifo.wrfull;
    assign rdreq_o      = data_word_cnt;
    
// ********************* ПРОЦЕССЫ ******************************************************************
    
   // Переключение состояний автомата (классическое описание)
    always_ff @(posedge amm_clk,  negedge reset_n)
    begin
        if (!reset_n) begin
            state_fifo_to_amm_burst   <= IDLE_s;
        end
        else begin
            state_fifo_to_amm_burst   <= next_state; 
        end
    end
    
    // Определение следующего состояния автомата
    always_comb        
    begin
        next_state = state_fifo_to_amm_burst; 
        unique case (state_fifo_to_amm_burst)
            IDLE_s:  
            begin
                if (start_i && (|data_size_i))
                    next_state  = WAIT_s;   
                else;
            end     

            WAIT_s:
            begin
                if (data_size_latch >= max_burst_p) begin
                    if (fifo.rdusedw >= max_burst_p)
                        next_state  = AMM_REQ_WR_BURST_s;
                    else;
                end
                else begin
                    next_state  = AMM_REQ_WR_BURST_s;
                end
            end
            
            AMM_REQ_WR_BURST_s:
            begin
                // if (!avm_waitrequest)
                    next_state  = AMM_WR_BURST_s;
                // else;
            end
            
            AMM_WR_BURST_s:
            begin
                if (end_burst) begin
                    if (end_data)
                        next_state  = IDLE_s;
                    else
                        next_state  = CALC_s;
                end
                else;
            end
            
            CALC_s:
            begin
                next_state  = WAIT_s;
            end
            
            default: ;
        endcase
    end   

    // Описание выходов автомата 
    always_comb
    begin
        avm_burstcount          = current_burstcount;
        avm_beginbursttransfer  = 1'b0;
        avm_write               = 1'b0;
        avm_address             = address;
        fifo.rdreq              = 1'b0;
        avm_writedata           = fifo.q;
        busy                    = 1'b1;
        reset_fifo              = 1'b0;
        unique case (state_fifo_to_amm_burst)
            IDLE_s:  
            begin
                busy    = 1'b0;
                reset_fifo  = start_i;
            end   
            
            AMM_REQ_WR_BURST_s:     
            begin
                avm_beginbursttransfer  = 1'b1;         // Запрос
                avm_write               = 1'b1;
                if (!avm_waitrequest) begin
                    fifo.rdreq      = 1'b1;
                end
                else;
            end
            
            AMM_WR_BURST_s:     
            begin
                if (!end_burst) begin
                    avm_write           = 1'b1;         // Подтверждение записи
                    if (!avm_waitrequest) begin
                        fifo.rdreq      = 1'b1;
                    end
                    else;
                end
                else;
            end
            
            default: ;
        endcase
    end

    // Счетчик адресов
    always_ff @(posedge amm_clk)
    begin
        unique case (state_fifo_to_amm_burst)
            IDLE_s:
            begin
                if (start_i) begin
                    data_word_cnt   <= data_size_i;
                    data_size_latch <= data_size_i;
                    address         <= base_addr_i;
                end
                else;
            end
            
            WAIT_s:
            begin
                if (data_size_latch >= max_burst_p) begin
                    if (fifo.rdusedw >= max_burst_p)
                        current_burstcount  <= max_burst_p;
                    else;
                end
                else begin
                    current_burstcount  <= fifo.rdusedw;
                end
            end
            
            AMM_REQ_WR_BURST_s:     
            begin
                end_burst       <= 1'b0;
                burst_word_cnt  <= 10'd0;
                if (avm_write && (!avm_waitrequest)) begin
                    burst_word_cnt  <= 10'd1;
                    data_word_cnt   <= data_word_cnt - 32'd1;
                end
                else;
            end

            AMM_WR_BURST_s:     
            begin
                if (avm_write && (!avm_waitrequest)) begin
                    end_burst       <= 1'b1;
                    burst_word_cnt  <= burst_word_cnt + 10'd1;
                    if (burst_word_cnt < current_burstcount - 1) begin
                        end_burst       <= 1'b0;
                    end
                    else;
              
                    end_data        <= 1'b1;
                    data_word_cnt   <= data_word_cnt - 32'd1;
                    if (data_word_cnt > 32'd1) begin
                        end_data        <= 1'b0;
                    end
                    else;
                end
                else;
            end
                                
            CALC_s:
            begin
                address <= address + (burst_word_cnt << 2);
            end
            default: ;
        endcase
    end
    
// ********************* ПОДКЛЮЧАЕМЫЕ МОДУЛИ *******************************************************
      // Буфер FIFO
    dcfifo  fifo_inst1
    (   
        .aclr                   (~reset_n | reset_fifo),
    
        // Запись в фифо
        .wrclk                  (data_clk),    
        .wrreq                  (fifo.wrreq),
        .data                   (fifo.data),
        .wrfull                 (fifo.wrfull),
        .wrusedw                (fifo.wrusedw),
      
        // Чтение из фифо
        .rdclk                  (amm_clk),
        .rdreq                  (fifo.rdreq),
        .q                      (fifo.q),
        .rdusedw                (fifo.rdusedw),
        .rdempty                (fifo.rdempty)
    );
    defparam
        fifo_inst1.lpm_numwords          = fifo_size_lp,   // Объем фифо
        fifo_inst1.lpm_showahead         = "ON",           // режим ASK включен
        fifo_inst1.lpm_type              = "dcfifo",
        fifo_inst1.lpm_width             = 32,             // Разрядность данных
        fifo_inst1.lpm_widthu            = fifo_wa_lp,     // Разрядность адреса

        fifo_inst1.overflow_checking     = "ON",
        fifo_inst1.rdsync_delaypipe      = 4,
        fifo_inst1.underflow_checking    = "ON",
        fifo_inst1.use_eab               = "ON",
        fifo_inst1.wrsync_delaypipe      = 4;
        
endmodule 